package shortcircuit.utilities;

import shortcircuit.basics.*;

import java.awt.Dimension;
import java.io.*;
import java.util.ArrayList;

import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;

/**
 * This class will take care of all of the reading and writing to files for this project.  Such implementations include
 * the reading of an original map in Puzzle Mode, the rendering of the map in Multiplayer Mode, and the saving of
 * records for unlocked levels in Puzzle Mode and the highest score in Endless Mode.
 * 
 * @author Kevin Lewi
 */

public class FileIO
{
	public static final int WRITE = 0, READ = 1;
	int _Operation;
	String filePath;
	private int numberOfMoves, blocksToRemove;
	public int[] powerUpArray;

	public int getNumberOfMoves() {
		return numberOfMoves;
	}

	public int getBlocksToRemove() {
		return blocksToRemove;
	}

	/**
	 * Initiates the reading or writing of files depending on the parameters passed to this constructor.
	 * 
	 * @param theFilePath The path to the file
	 * @author Kevin Lewi
	 */
	public FileIO(String theFilePath)
	{
		filePath = theFilePath;
	}

	/**
	 * Initiates the reading or writing of files depending on the parameters passed to this constructor.
	 * 
	 * @param theOperation The operation to be executed
	 * @author Kevin Lewi
	 */
	public FileIO(int theOperation)
	{
		_Operation = theOperation;
	}

	/**
	 * Initiates the reading or writing of files depending on the parameters passed to this constructor.
	 * 
	 * @param theFilePath The path to the file
	 * @param theOperation The operation to be executed
	 * @author Kevin Lewi
	 */
	public FileIO(String theFilePath, int theOperation)
	{
		filePath = theFilePath;
		_Operation = theOperation;
	}

	/**
	 * Takes the text directly from a file, decrypts, and then translates it into an Environment containing blocks set
	 * by the file.<p>
	 * This method also calls convertTextToBlock to translate the text into
	 * a format that can be used in an Environment.
	 * 
	 * @return The resulting environment described by the file
	 * @author Kevin Lewi
	 */
	public Environment readMap()
	{
		Environment tempEnv;
		try
		{
			Debug.on();
			BufferedReader inputReader = new BufferedReader(new FileReader(filePath));
			String fileData = "";
			while(inputReader.ready())
				fileData += inputReader.readLine() + "\n";
			//fileData = decrypt(fileData);

			tempEnv = new Environment(
					new Dimension(Integer.parseInt(fileData.substring(0, fileData.indexOf(" "))), 
							Integer.parseInt(fileData.substring(fileData.indexOf(" ") + 1, fileData.indexOf("\n")))));
			fileData = fileData.substring(fileData.indexOf("\n") + 1);

			numberOfMoves = Integer.parseInt(fileData.substring(0, fileData.indexOf(" ")));
			blocksToRemove = Integer.parseInt(fileData.substring(fileData.indexOf(" ") + 1, fileData.indexOf("\n")));
			fileData = fileData.substring(fileData.indexOf("\n") + 1);

			int cellCount = 0;
			for(int index = 0; index < tempEnv.getGrid()[0].length - 1; index++)
			{
				String currentLine = fileData.substring(0, fileData.indexOf("\n"));
				for(int lineReader = 0; lineReader < currentLine.length(); lineReader++)
				{
					String currentChar = currentLine.substring(lineReader, lineReader + 1);
					if(!currentChar.equals(" "))
					{
						try
						{
							tempEnv.replaceCellContents(cellCount, Integer.parseInt(currentChar));
							cellCount++;
						}
						catch(NumberFormatException e) { Debug.println("Number Format Exception"); }
					}
				}
				fileData = fileData.substring(2 * tempEnv.getGrid().length);
			}
			
			powerUpArray = new int[3];
			
			for(int i = 0; i < 3; i++)
			{
				if(i == 2)
				{
					powerUpArray[i] = Integer.parseInt(fileData.substring(0, fileData.indexOf("\n")));
					fileData = fileData.substring(fileData.indexOf("\n") + 1);
				}
				else
				{
					powerUpArray[i] = Integer.parseInt(fileData.substring(0, fileData.indexOf(" ")));
					fileData = fileData.substring(fileData.indexOf(" ") + 1);
				}
			}
		}
		catch(FileNotFoundException e) { Debug.println("Error: File Not Found"); return null;}
		catch(IOException e) { Debug.println("Error: IO"); return null;}
		Debug.on();
		return tempEnv;
	}

	/**
	 * Reads the data from the specified file and returns the regular text.
	 * 
	 * @return The decrypted text
	 * @author Kevin Lewi
	 */
	String readData()
	{
		String totalText = "";
		try
		{
			BufferedReader inputReader = new BufferedReader(new FileReader(filePath));
			while(inputReader.ready())
				totalText += inputReader.readLine() + "\n";
		}
		catch(FileNotFoundException e) { Debug.println("Error: File Not Found"); return null;}
		catch(IOException e) { Debug.println("Error: IO"); return null;}

		return totalText;//decrypt(totalText);
	}

	public void setHighScore(int highScore)
	{
		String s = readData();
		s = s.substring(s.indexOf("\n") + 1);
		s = highScore + "\n" + s;
		writeData(s);
	}

	public void setLevelStatus(int theLevel, int theStatus)
	{
		String s = readData();
		String t = "";
		for(int i = 0; i < theLevel; i++)
		{
			t += s.substring(0, s.indexOf("\n")) + "\n";
			s = s.substring(s.indexOf("\n") + 1);
		}
		t += theStatus + "\n" + s.substring(s.indexOf("\n") + 1);
		writeData(t);
	}

	public int getHighScore()
	{
		String s = readData();
		return Integer.parseInt(s.substring(0, s.indexOf("\n")));
	}

	public ArrayList<Integer> getLevelStatuses()
	{
		ArrayList<Integer> theList = new ArrayList<Integer>();
		String s = readData();
		s = s.substring(s.indexOf("\n") + 1);
		while(s.contains("\n"))
		{
			theList.add(Integer.parseInt(s.substring(0, s.indexOf("\n"))));
			s = s.substring(s.indexOf("\n") + 1);
		}
		return theList;
	}
	
	public int getLevelStatus(int theLevel)
	{
		ArrayList<Integer> tempArray = getLevelStatuses();
		return tempArray.get(theLevel - 1);
	}

	/**
	 * Writes encrypted text directly into the specified file.
	 * 
	 * @author Kevin Lewi
	 */
	void writeData(String theFileText)
	{
		try
		{
			BufferedWriter outputWriter = new BufferedWriter(new FileWriter(filePath));
			//theFileText = encrypt(theFileText);
			outputWriter.write(theFileText);
			outputWriter.close();
		}
		catch(IOException e) { Debug.println("Error: IO"); }
	}

	/**
	 * Sets the file path of the file reader using the passed in parameter.
	 * 
	 * @param theFilePath The path of the file
	 * @author Kevin Lewi
	 */
	void setFilePath(String theFilePath)
	{
		filePath = theFilePath;
	}

	/**
	 * Sets the operation of the file reader using the passed in parameter.
	 * 
	 * @param theOperation The type of operation the class will execute
	 * @author Kevin Lewi
	 */
	void setOperation(int theOperation)
	{
		_Operation = theOperation;
	}

	/**
	 * Gets the saved key from the key file that is used to unlock maps.
	 * 
	 * @param filePath The path of the key file
	 * @return The SecretKey object stored in the file
	 */
	private SecretKey getKey(String filePath)
	{
		try
		{
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
			DESKeySpec ks = new DESKeySpec((byte[]) ois.readObject());
			SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
			return skf.generateSecret(ks);
		}
		catch(Exception e) {}
		return null;
	}

	/**
	 * Turns an a regular string of text into an encrypted string of text.
	 * 
	 * @param theUnencryptedString The regular text
	 * @return The encrypted text
	 * @author Kevin Lewi
	 */
	public String encrypt(String theUnencryptedString)
	{
		try
		{
			Cipher ecipher = Cipher.getInstance("DES");
			ecipher.init(Cipher.ENCRYPT_MODE, getKey("key"));

			// Encode the string into bytes using utf-8
			byte[] utf8 = theUnencryptedString.getBytes("UTF8");

			// Encrypt
			byte[] enc = ecipher.doFinal(utf8);

			// Encode bytes to base64 to get a string
			return new sun.misc.BASE64Encoder().encode(enc);
		}
		catch (javax.crypto.BadPaddingException e) {}
		catch (IllegalBlockSizeException e) {}
		catch (java.io.IOException e) {}
		catch (javax.crypto.NoSuchPaddingException e) {}
		catch (java.security.NoSuchAlgorithmException e) {}
		catch (java.security.InvalidKeyException e) {}
		return null;
	}

	/**
	 * Turns an an encrypted string of text into a regular string of text.
	 * 
	 * @param theDecryptedString The encrypted text
	 * @return The regular text
	 * @author Kevin Lewi
	 */
	public String decrypt(String theDecryptedString)
	{
		try
		{


			Cipher dcipher = Cipher.getInstance("DES");
			dcipher.init(Cipher.DECRYPT_MODE, getKey("key"));

			// Decode base64 to get bytes
			byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(theDecryptedString);

			// Decrypt
			byte[] utf8 = dcipher.doFinal(dec);

			// Decode using utf-8
			return new String(utf8, "UTF8");
		}
		catch (javax.crypto.BadPaddingException e) { System.out.println("1"); }
		catch (IllegalBlockSizeException e) { System.out.println("2"); }
		catch (java.io.IOException e) { System.out.println("3"); }
		catch (javax.crypto.NoSuchPaddingException e) { System.out.println("4"); }
		catch (java.security.NoSuchAlgorithmException e) { System.out.println("5"); }
		catch (java.security.InvalidKeyException e) { System.out.println("6"); }
		catch (Exception e) { System.out.println("7"); }
		return null;
	}

}

